---
title: 요청 결합하기
version: 2
---

import { Link } from "/src/components/Link";
import { AutoSnippet } from "/src/components/CodeSnippet";
import functionalRef from "/docs/essentials/combining_requests/functional_ref";
import notifierRef from "/docs/essentials/combining_requests/notifier_ref";
import watchExample from "/docs/essentials/combining_requests/watch_example";
import watchPlacement from "/docs/essentials/combining_requests/watch_placement";
import listenExample from "/docs/essentials/combining_requests/listen_example";
import readExample from '/docs/essentials/combining_requests/read_example'

지금까지는 요청이 서로 독립적인 경우만 보았습니다. 
하지만 일반적인 사용 사례는 다른 요청의 결과에 따라 요청을 트리거해야 하는 경우입니다.

이를 위해 provider의 결과를 다른 provider에게 매개변수(parameter)로 전달하여 <Link documentID="essentials/passing_args" /> 메커니즘을 _사용할 수_ 있습니다.

하지만 이 접근 방식에는 몇 가지 단점이 있습니다:

- 구현 세부 정보가 유출(leaks)됩니다.
  이제 UI는 다른 provider가 사용하는 모든 providers에 대해 알아야 합니다.
- 매개변수(parameter)가 변경될 때마다 완전히 새로운 상태가 만들어집니다.
  매개변수를 전달하면 매개변수가 변경될 때 이전 상태를 유지할 수 있는 방법이 없습니다.
- 요청을 결합(combining requests)하기가 더 어려워집니다.
- 따라서 툴링의 유용성이 떨어집니다. 개발 도구는 providers 간의 관계에 대해 알 수 없습니다.

이를 개선하기 위해 Riverpod은 요청을 결합하는 다른 접근 방식을 제공합니다.

## 기본 사항: "ref" 획득하기

요청을 결합하는 모든 방법에는 한 가지 공통점이 있습니다. 모두 `Ref` 객체를 기반으로 한다는 점입니다.

`Ref` 객체는 모든 providers가 접근할 수 있는 객체입니다.
이 객체는 다양한 라이프사이클 리스너에 대한 액세스 권한을 부여할 뿐만 아니라, providers를 결합하는 다양한 메서드도 제공합니다.

`Ref`를 얻을 수 있는 위치는 provider 타입에 따라 다릅니다.

함수형 provider의 경우, `Ref`는 provider의 함수에 매개변수로 전달됩니다:

<AutoSnippet 
  {...functionalRef} 
  translations={{
    provider: "  // \"Ref\"를 사용하여 다른 providers를 읽을 수 있습니다.",
  }}
/>

클래스 변형에서 `Ref`는 Notifier 클래스의 속성입니다:

<AutoSnippet 
  {...notifierRef} 
  translations={{
    watch: "    // \"Ref\"를 사용하여 다른 providers를 읽을 수 있습니다.",
  }}
/>

## ref를 사용하여 provider를 읽습니다.

## `ref.watch` 메소드

이제 `Ref`를 얻었으므로 이를 사용하여 요청을 결합할 수 있습니다.
이를 수행하는 주된 방법은 `ref.watch`를 사용하는 것입니다.  
일반적으로 유지 관리가 더 쉽기 때문에 일반적으로 다른 옵션보다 `ref.watch`를 사용할 수 있도록 코드를 설계하는 것이 좋습니다.


`ref.watch` 메서드는 provider를 받아 현재 상태를 반환합니다.
그러면 리스닝된 provider가 변경될 때마다 provider가 무효화(invalidated)되고 다음 프레임 또는 다음 읽기(read) 시 다시 빌드됩니다.

`ref.watch`를 사용하면 로직이 "reactive"이면서 "declarative"이게 됩니다.  
즉, 필요할 때 로직이 자동으로 다시 계산(recompute)된다는 뜻입니다.
그리고 업데이트 메커니즘이 'on change'와 같은 부작용(side-effects)에 의존하지 않습니다.
이는 StatelessWidgets의 작동 방식과 유사합니다.

예를 들어 사용자의 위치를 수신하는 provider를 정의할 수 있습니다.
그런 다음 이 위치를 사용하여 사용자 근처의 레스토랑 목록을 가져올 수 있습니다.

<AutoSnippet 
  {...watchExample} 
  translations={{
    provider: "  // TO-DO: 현재 위치를 가져오는 스트림을 반환합니다.",
    watch: "  // \"ref.watch\"를 사용하여 최신 위치를 가져옵니다.\n  // provider 뒤에 \".future\"를 지정하면 코드가 적어도 하나의 위치를 사용할 수 있을 때까지 기다립니다.",
    get: "  // 이제 해당 위치를 기반으로 네트워크 요청을 할 수 있습니다.\n  // 예를 들어 Google 지도 API를 사용할 수 있습니다:",
    jsonDecode: "  // JSON에서 레스토랑 이름 가져오기",
  }}
/>

:::info
수신 중인 provider가 변경되어 요청이 다시 계산되면 새 요청이 완료될 때까지 이전 상태가 유지됩니다.  
동시에 요청이 보류(pending)되는 동안 "isLoading" 및 "isReloading" 플래그가 설정됩니다.

이를 통해 UI에 이전 상태 또는 로딩 표시기를 표시하거나 둘 다 표시할 수 있습니다.
:::

:::info
`ref.watch(locationProvider)` 대신 `ref.watch(locationProvider.future)`를 사용한 것을 주목하세요.
`locationProvider`가 비동기적이기 때문입니다. 따라서 초기 값을 사용할 수 있을 때까지 기다려야 합니다.

이 `.future`를 생략하면 `locationProvider`의 현재 상태에 대한 스냅샷인 `AsyncValue`를 받게 됩니다. 
하지만 아직 사용할 수 있는 위치가 없다면 아무 것도 할 수 없습니다.
:::

:::caution
"명령형 문법(imperatively)"으로 실행되는 코드 내에서 `ref.watch`를 호출하는 것은 나쁜 습관으로 간주됩니다. 
이는 provider의 빌드 단계에서 실행되지 않을 가능성이 있는 모든 코드를 의미합니다. 
여기에는 "listener" 콜백(callbacks)이나 Notifier의 메서드가 포함됩니다:

<AutoSnippet 
  {...watchPlacement} 
  translations={{
  }}
/>
:::

## `ref.listen`/`listenSelf` 메소드

`ref.listen` 메서드는 `ref.watch`의 대안입니다.  
이 메서드는 기존의 "listen"/"addListener" 메서드와 유사합니다. 
이 메서드는 provider와 callback을 받으며, provider의 콘텐츠가 변경될 때마다 해당 callback을 호출합니다.

`ref.listen` 대신 `ref.watch`를 사용할 수 있도록 코드를 리팩토링하는 것이 일반적으로 권장되는데, 
전자는 명령형으로 인해 오류가 발생하기 쉽기 때문입니다.  
하지만 `ref.listen`는 큰 리팩토링을 하지 않고도 빠른 로직을 추가하는 데 유용할 수 있습니다.

`ref.watch` 예제를 다시 작성하여 `ref.listen`을 대신 사용할 수 있습니다.

<AutoSnippet 
  {...listenExample} 
  translations={{
  }}
/>

:::info
provider의 빌드 단계에서 `ref.listen`을 사용하는 것은 전적으로 안전합니다.
provider가 어떻게든 다시 계산되면 이전 리스너가 제거됩니다.

또는 `ref.listen`의 반환 값을 사용하여 원할 때 리스너를 수동으로 제거할 수 있습니다.
:::

## `ref.read` 메소드

마지막으로 사용할 수 있는 옵션은 `ref.read`입니다.
이 옵션은 provider의 현재 상태를 반환한다는 점에서 `ref.watch`와 유사합니다.
하지만 `ref.watch`와 달리 provider를 수신(listen)하지 않습니다.

따라서 `ref.read`는 Notifier의 메서드 내부와 같이 `ref.watch`를 사용할 수 없는 곳에서만 사용해야 합니다.

<AutoSnippet 
  {...readExample} 
  translations={{
    read: "    // Bad! 반응형이 아니므로 여기서는 'read'를 사용하지 마십시오.",
    read2: "    ref.read(otherProvider); // 여기서 '읽기'를 사용해도 괜찮습니다.",
  }}
/>

:::caution
provider에서 `ref.read`를 사용할 때는 주의하세요. provider를 수신(listen)하지 않으므로, 해당 provider가 수신(listen)하지 않으면 상태(state)를 파괴(destroy할 수 있습니다.
:::
